#ifndef __MODEL_H__
#define __MODEL_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <sstream>
#include <math.h>
#include <assert.h>
#include <limits.h>
#include "types.h"
#include "maps.h"
#include "image.h"
#include "weights.h"
#include "external/fire/gmd.hpp"
#include "external/fann/doublefann.h"
#include "external/fann/fann_cpp.h"

class Feature {
private:
	typedef std::vector<float> vfloat_t;
	vfloat_t _vItems;
public:
	Feature();
	Feature(int size);
	void resize(int size);
	float getItem(int k) const;
	void	setItem(int k, float f);
	int getSize() const;
	float getDistance(const Feature& f) const;
	float getEukDistance(const Feature& f) const;
};
class NNFeature: public Feature {
public:
	NNFeature();
	NNFeature(const Image& i, int x, int y);
	void initialise(const Image& i, int x, int y);
};
class Model {
public:
	Model();
	virtual ~Model();
	virtual void train(const Trimap & t) = 0;
	virtual void setImage(Image) = 0;
	virtual Weights getWeights(uint, uint, const Trimap & t) = 0;
	virtual void getWeights(uint, uint, Weights&, const Trimap & t) = 0;
	virtual void reset() = 0;
	virtual void setEdgeWeight(float w) = 0;
	virtual float getEdgeWeight() const = 0;
	virtual void setSosiWeight(float w) = 0;
	virtual float getSosiWeight() const = 0;
	virtual void setClassProbs(float o, float b) = 0;
};
class BaseModel: public Model {
protected:
	Image _oImage;
	bool _bRecalcObject;
	float _fEdgeWeight,
				_fSosiWeight,
				_fPo,_fPb;
public:
	BaseModel();
	void setImage(Image i);
	void train(const Trimap & t);
	Weights getWeights(uint xo, uint yo, const Trimap & t);
	void getWeights(uint xo, uint yo, Weights& w, const Trimap & t);
	void reset();
	void setEdgeWeight(float w);
	float getEdgeWeight() const;
	void setSosiWeight(float w);
	float getSosiWeight() const;
	void setClassProbs(float o, float b);
};

class SimpleModel:public BaseModel {
	void getWeights(uint x, uint y, Weights& w, const Trimap & t);
	void train(const Trimap & t);
};
class NNModel:public BaseModel {
private:
	typedef std::vector<NNFeature> vfeature_t;
	vfeature_t _vObject;
	vfeature_t _vBackground;
	float calcProb(const NNFeature& p, const vfeature_t& v) const;
public:
	NNModel();
	void train(const Trimap & t);
	void getWeights(uint x, uint y, Weights& w, const Trimap & t);
	void reset();
};

class GmdModel:public BaseModel {
protected:
	GaussMixtureDensity *_oGmdObject,
											*_oGmdBackground;
	FeatureVector* getFeatureVector(int x, int y, const Trimap &) const;
public:
	GmdModel();
	~GmdModel();
	void train(const Trimap & t);
	float	getObjectProb(uint x, uint y, const Trimap & t) const;
	void getWeights(uint x, uint y, Weights& w, const Trimap & t);
	void reset();
};
class Gmd2Model:public GmdModel {
public:
	float	getObjectProb(uint x, uint y, const Trimap & t) const;
	void getWeights(uint x, uint y, Weights& w, const Trimap & t);
};

class FannModel:public BaseModel{
private:
	FANN::neural_net _oFann;
	fann_type * createFannType(int x, int y);
public:
	FannModel();
	void train(const Trimap & t);
	void getWeights(uint x, uint y, Weights& w, const Trimap & t);
	void reset();
};
#endif
